#include "xncc_capture.h"
#include <arpa/inet.h>
#include <asm-generic/socket.h>
#include <assert.h>
#include <cstddef>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <linux/filter.h>
#include <linux/if_packet.h>
#include <linux/sockios.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

xncc_capture::xncc_capture() : sock_raw_fd_(-1), buffer_(nullptr), bind_interface_("")
{
}
xncc_capture::xncc_capture(const std::string& if_name) : sock_raw_fd_(-1), buffer_(nullptr), bind_interface_(if_name)
{
}

xncc_capture::~xncc_capture()
{
    munmap(buffer_, NUM_FRAMES * STABEL_FRAME_SIZE);
    close(sock_raw_fd_);
}

int xncc_capture::iface_get_mtu(char* ebuf)
{
    struct ifreq ifr;
    if (bind_interface_.empty())
        return STABEL_FRAME_SIZE;
    memset(&ifr, 0, sizeof(ifr));
    strncpy(ifr.ifr_name, bind_interface_.c_str(), sizeof(ifr.ifr_name));
    if (ioctl(sock_raw_fd_, SIOCGIFMTU, &ifr) == -1) {
        // pcap_fmt_errmsg_for_errno(ebuf, PCAP_ERRBUF_SIZE, errno, "SIOCGIFMTU");
        return -1;
    }
    return ifr.ifr_mtu;
}

bool xncc_capture::open()
{
    sock_raw_fd_ = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
    if (sock_raw_fd_ == -1) {
        printf("create socket failed, err:%s\n", strerror(errno));
        return false;
    }
    int val = TPACKET_V2;
    if (setsockopt(sock_raw_fd_, SOL_PACKET, PACKET_VERSION, &val, sizeof(val))) {
        printf("set  socket PACKET_VERSION failed, err:%s\n", strerror(errno));
        return false;
    }
    if (!bind_interface_.empty()) {
        if (bind_interface_.size() >= IF_NAMESIZE) {
            return false;
        }
        if (setsockopt(sock_raw_fd_, SOL_SOCKET, SO_BINDTODEVICE, bind_interface_.c_str(), bind_interface_.size())) {
            printf("set  socket SO_BINDTODEVICE failed, err:%s\n", strerror(errno));
            return false;
        }
    }

    /* tp_block_size / tp_frame_size * tp_block_nr == tp_frame_nr */
    struct tpacket_req req;
    if (BIGGER_ALL_MTU) {
        req.tp_frame_size = STABEL_FRAME_SIZE;  //必须16字节对齐
        req.tp_frame_nr   = NUM_FRAMES;
        req.tp_block_size = STABEL_FRAME_SIZE;  //每个内存块的大小 页面大小的整数倍
        req.tp_block_nr   = (req.tp_frame_size * req.tp_frame_nr) / req.tp_block_size;  //内存块的数量
    }
    else {
        req.tp_frame_size = COMMON_FRAME_SIZE;  //必须16字节对齐
        req.tp_frame_nr   = NUM_FRAMES;
        req.tp_block_size = COMMON_FRAME_SIZE * 2;  //页面大小的整数倍 4096 先设置为4096
        req.tp_block_nr   = (req.tp_frame_size * req.tp_frame_nr) / req.tp_block_size;  //内存块的数量
    }
    cur_frame_size_ = req.tp_frame_size;
    assert(req.tp_block_size / req.tp_frame_size * req.tp_block_nr == req.tp_frame_nr);
    if (setsockopt(sock_raw_fd_, SOL_PACKET, PACKET_RX_RING, reinterpret_cast<const void*>(&req), sizeof(req))) {
        printf("set  socket PACKET_RX_RING failed, err:%s\n", strerror(errno));
        return false;
    }
    auto ret = mmap(NULL, req.tp_block_size * req.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, sock_raw_fd_, 0);
    if (ret == MAP_FAILED) {
        printf("mmap failed , err:%s\n", strerror(errno));
        return false;
    }
    buffer_ = static_cast<char*>(ret);
    return true;
}

void xncc_capture::receive()
{
    int64_t index = 0;
    while (true) {
        if (NEED_POLL) {
            if (ring_poll()) {
                read_sock_raw(index);
            }
        }
        else {
            read_sock_raw(index);
        }
    }
}

void xncc_capture::read_sock_raw(int64_t& index)
{
    if (buffer_ != nullptr) {
        struct tpacket2_hdr* header =
            reinterpret_cast<struct tpacket2_hdr*>(buffer_ + ((index % NUM_FRAMES) * cur_frame_size_));
        if (header->tp_status & TP_STATUS_USER) {
            const char* data = reinterpret_cast<char*>(header) + header->tp_mac;
            if (cap_cb_) {
                cap_cb_(data);
            }
            header->tp_status = TP_STATUS_KERNEL;
            index++;
        }
    }
}
long long xncc_capture::gettimeofday_ms(void)
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}

bool xncc_capture::ring_poll()
{
    struct pollfd pfd;
    pfd.fd     = sock_raw_fd_;
    pfd.events = POLLIN;
    int ret    = poll(&pfd, 1, 100);
    if (ret == -1) {
        printf("poll error %s\n", strerror(errno));
    }
    else if (ret == 0) {  // timeout
        return false;
    }
    else if (pfd.revents != POLLIN) {
        printf("poll event error  %d\n", pfd.revents);
    }
    return true;
}